package ssl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.io.File;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Enumeration;
import java.util.Random;
import java.util.Scanner;

/**
 * 服务器绑定证书生成工具
 */
public class ServerBoundCertificateGenerator {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public static void main(String[] args) throws Exception {

        // 注册 Bouncy Castle 提供程序
        Security.addProvider(new BouncyCastleProvider());

        Scanner scanner = new Scanner(System.in);

        System.out.println("=== 服务器绑定证书生成工具 ===");
        System.out.print("请输入密钥库密码(不输入将随机生成): ");
        String keystorePassword = scanner.nextLine();
        if (keystorePassword.isEmpty()) {
            keystorePassword = generateRandomEnvVarName();
        }

        System.out.print("请输入证书别名(默认server): ");
        String alias = scanner.nextLine();
        if (alias.isEmpty()) alias = "server";

        // 获取服务器唯一标识
        String serverId = getServerUniqueId();
        System.out.println("检测到服务器ID: " + serverId);

        // 生成证书
        generateServerBoundCertificate(alias, keystorePassword, serverId);

        System.out.println("✅ 证书生成完成！");
        System.out.println("password: " + keystorePassword);
        scanner.close();
    }

    // 新增: 生成完全随机的字符
    private static String generateRandomEnvVarName() {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 20; i++) { // 生成20个字符长度的随机字符串
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }

    private static String getServerUniqueId() throws Exception {
        // 组合多个硬件信息作为唯一标识
        String hostName = InetAddress.getLocalHost().getHostName();

        // 遍历所有网络接口，找到第一个有效的MAC地址
        byte[] macBytes = null;
        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        while (networkInterfaces.hasMoreElements() && macBytes == null) {
            NetworkInterface ni = networkInterfaces.nextElement();
            if (ni.isUp() && !ni.isLoopback() && ni.getHardwareAddress() != null) {
                macBytes = ni.getHardwareAddress();
            }
        }

        // 如果没有找到有效的MAC地址，使用主机名的哈希值
        if (macBytes == null) {
            System.out.println("警告: 无法获取有效的MAC地址，使用主机名的哈希值作为替代");
            macBytes = DigestUtils.sha256(hostName.getBytes());
        }

        StringBuilder macAddress = new StringBuilder();
        for (byte b : macBytes) {
            macAddress.append(String.format("%02X", b));
        }
        return DigestUtils.sha256Hex(hostName + macAddress).replaceAll("[^a-zA-Z0-9]", ""); // 清理不可见字符
    }

    private static void generateServerBoundCertificate(
            String alias, String password, String serverId) throws Exception {

        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "BC");
        keyGen.initialize(2048);
        KeyPair keyPair = keyGen.generateKeyPair();

        // 证书有效期
        Date startDate = new Date();
        Date endDate = new Date(startDate.getTime() + 365 * 24 * 60 * 60 * 1000L);

        // 证书信息
        X500Name issuer = new X500Name("CN=" + alias + ", O=MyOrg, C=CN");
        BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());

        // 使用JcaX509v3CertificateBuilder替代X509v3CertificateBuilder
        JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
                issuer,
                serial,
                startDate,
                endDate,
                issuer,  // 这里subject和issuer相同（自签名证书）
                keyPair.getPublic());

        // 添加服务器绑定扩展
        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();

        // 构造符合 GeneralName.otherName 格式的对象
        ASN1ObjectIdentifier otherNameType = new ASN1ObjectIdentifier("1.3.6.1.4.1.311.21.7"); // 示例 OID
        DERUTF8String serverIdValue = new DERUTF8String(serverId);
        GeneralName otherName = new GeneralName(GeneralName.otherName, new org.bouncycastle.asn1.x509.OtherName(otherNameType, serverIdValue));
        GeneralNames subjectAlternativeNames = new GeneralNames(otherName);

        certBuilder.addExtension(
                Extension.subjectAlternativeName,
                false,
                subjectAlternativeNames);

        // 签名
        ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA")
                .setProvider("BC")
                .build(keyPair.getPrivate());

        X509CertificateHolder certHolder = certBuilder.build(signer);

        // 转换证书
        X509Certificate cert = new JcaX509CertificateConverter()
                .setProvider("BC")
                .getCertificate(certHolder);

        // 保存为PKCS12格式
        KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
        keyStore.load(null, null);
        keyStore.setKeyEntry(
                alias,
                keyPair.getPrivate(),
                password.toCharArray(),
                new X509Certificate[]{cert});

        String fileName = alias + "_bound.p12";
        try (FileOutputStream fos = new FileOutputStream(fileName)) {
            keyStore.store(fos, password.toCharArray());
        }

        //生成验证json
        JSONObject entries = new JSONObject();
        entries.set("path", new File(fileName).getAbsolutePath());
        entries.set("passwordKey", generateRandomEnvVarName());
        // 新建文件
        String jsonFile = "authentication.json";
        FileUtil.touch(jsonFile);
        FileUtil.writeUtf8String(entries.toString(), jsonFile);

        System.out.println("生成的证书文件: " + new File(fileName).getAbsolutePath());
        System.out.println("生成的鉴权文件: " + new File(jsonFile).getAbsolutePath());
    }
}

